# !/usr/bin/env python

# Chat Server

# Advice:
""" 
 1. If the client pressed "ctrl+c" to exit the chat env, do it can tell the chat server that he is 
 disconnected. And the chat server should display the client exit info, broadcast other client that the client 
 is disconnected.

 2. Do the client can input the keyword "exit or quit" to exit the chat env ?  
"""

# Select module async IO
import socket, select

# Function to broadcast chat messages to other connected clients
def broadcast_data(sock, message):
	# Do not send the messages to master server and the client who has send the message
	for socket in CONNECTION_LIST:
		if socket != server_socket and socket != sock:
			try:
				socket.send(message)
			except:
				# Broken socket connection may be , chat client pressed ctrl+c 
				socket.close()
				CONNECTION_LIST.remove(socket)

if __name__ == "__main__":
	# List to keep track of socket descriptors
	CONNECTION_LIST = []
	RECV_BUFFER = 4096 # Advisable to keep it as exponent of 2
	PORT = 5000

	server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
	server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
	server_socket.bind(("0.0.0.0", PORT))
	server_socket.listen(10)

	# Add server socket to the list of readable connections
	CONNECTION_LIST.append(server_socket)
	print "Chat server started on port" + str(PORT)

	while 1:
		# Get the list sockets which are ready to be read through select
		read_sockets, write_sockets, error_sockets = select.select(CONNECTION_LIST, [], [], 2)

		for sock in read_sockets:
			if sock == server_socket:
				sockfd, addr = server_socket.accept()
				CONNECTION_LIST.append(sockfd)
				print "Client (%s %s) connnected" % addr 

				broadcast_data(sockfd, "[%s:%s] entered room\n" % addr)

			# Some message from a client
			else:
				# Data received from client, process it
				try:
					data = sock.recv(RECV_BUFFER)
					if data:
						broadcast_data(sock, "\r" + '<' + str(sock.getpeername()) + '>' + data)

				except:
					broadcast_data(sock, "Client (%s, %s) is offline" % addr)
					print "Client (%s, %s) is offline" % addr
					sock.close()
					CONNECTION_LIST.remove(sock)
					# print "Client (%s %s) disconnected" % addr
					# broadcast_data(sock, "Client (%s %s) is disconnected!" % addr)
					continue


	server_socket.close()